package com.shujia.spark.mllib

import org.apache.spark.ml.feature.LabeledPoint
import org.apache.spark.ml.linalg
import org.apache.spark.ml.linalg.Vectors
import org.apache.spark.sql.{DataFrame, SparkSession}


object Demo01Vectors {
  def main(args: Array[String]): Unit = {

    // Spark中的向量：存储多个特征值，能够被机器学习模型识别
    // 在Spark中进行数据特征工程的目的：将各类数据转换成向量

    /**
     * Spark的MLLib模块下有两套API：
     * ml包下的：基于DF
     * mllib包下的：基于RDD
     */
    /**
     * 向量Vectors分为两类：
     * dense：稠密向量，每一个特征值都会保存
     * sparse：稀疏向量，只需要保存有值的特征，省略没有值的特征
     * 如何选择？
     * 如果特征值缺失的比较多，选择稀疏向量，反之选择稠密向量
     */
    val denseVec01: linalg.Vector = Vectors.dense(1, 0, 0, 1, 1, 0, 0, 0, 2, 3, 0, 0, 5)
    println(s"稠密向量：$denseVec01")

    /**
     * 创建稀疏向量需要三个参数：
     * size：向量的大小
     * indices：有特征值的下标位置
     * values：特征值
     */
    val sparseVec01: linalg.Vector = Vectors.sparse(13, Array(0, 3, 4, 8, 9, 12), Array(1, 1, 1, 2, 3, 5))
    println(s"稀疏向量：$sparseVec01")

    // 两种向量间的转换
    println(s"稠密向量转稀疏向量：${denseVec01.toSparse}")
    println(s"稀疏向量转稠密向量：${sparseVec01.toDense}")

    // Spark表示标签：通过构建LabelPoint来表示
    // 表示输入机器学习模型中完整的一条数据：一个标签值，一组特征值
    val labeledPoint: LabeledPoint = LabeledPoint(1, denseVec01)
    println(labeledPoint)

    // libsvm格式数据：0 1:5.3 2:3.5 3:2.5 4:106.4 5:67.5 6:69.1 7:83
    val lp: LabeledPoint = LabeledPoint(0, Vectors.dense(5.3, 3.5, 2.5, 106.4, 67.5, 69.1, 83))
    println(lp)

    val spark: SparkSession = SparkSession
      .builder()
      .appName(this.getClass.getSimpleName.replace("$", ""))
      .master("local") // 设置运行的方式
      .getOrCreate()

    // SparkSQL加载libsvm格式的数据
    val libsvmDF: DataFrame = spark
      .read
      .format("libsvm")
      .load("spark/data/mllib/data/personIndex.txt")

    libsvmDF.printSchema() // 可以将DF的结构打印出来

    libsvmDF.show()


  }

}
